/*
 * @Author: lizhil
 * @Date: 2021-08-09 13:33:43
 * @Last Modified by: lizhil
 * @Last Modified time: 2021-12-31 14:59:55
 */
const _ = require('lodash')
const dayjs = require('dayjs')

module.exports = (app) => {
  const mongoose = app.mongoose
  const { prefix } = app.config.jax.mongoose
  // const { helper } = app.ctx

  return function (...args) {
    const modelName = args[0]
    const schema = args[1]
    const config = {
      id: 'id',
      ...args[4],
    }

    const idKey = config.id

    if (idKey != 'id') {
      schema.virtual(idKey).get(function () {
        return this._id
      })
    }

    schema.set('toJSON', {
      transform: function (doc, ret, options) {
        ret.createdAt = dayjs(ret.createdAt).format('YYYY-MM-DD HH:mm:ss')
        ret.updatedAt = dayjs(ret.updatedAt).format('YYYY-MM-DD HH:mm:ss')
        delete ret._id
        delete ret.__v
        if (idKey != 'id') delete ret.id
      },
    })

    // 设置数据库中模型名称
    if (!args[2]) args[2] = (prefix || 'jax') + '_' + _.snakeCase(modelName)

    const Model = mongoose.model(...args)

    /**
     * 分页查询
     * @param {Object} [filter] 查询条件
     * @param {Object} [options] 查询选项
     * @param {Object} [options.page=1] 当前页数
     * @param {Object} [options.pageSize=20] 查询数量
     */
    Model.findPage = async (...args) => {
      let filter = {}
      let options = {
        page: 1,
        pageSize: 20,
        sort: {
          _id: '-1',
        },
      }

      // 兼容老项目写法，后期废弃
      if (args[0] && args[0].where) {
        console.warn('findPage where 已废弃，请使用filter')
        filter = args[0].where
        options = {
          ...options,
          ...args[0],
          fn: args[1],
        }
      } else {
        filter = args[0]
        if (typeof args[1] == 'function') {
          options = {
            ...options,
            fn: args[1],
          }
        } else {
          options = {
            ...options,
            ...args[1],
          }
        }
      }

      const { fn } = options
      const page = Number(options.page)
      const pageSize = Number(options.pageSize)

      // 获取当前数据表的总数量
      const total = await Model.find(filter).count()

      let find = Model.find(filter)

      if (fn) find = fn(find)

      Object.keys(options).map((key) => {
        if (find[key]) find = find[key](options[key])
      })

      if (pageSize != -1) find = find.skip((page - 1) * pageSize).limit(pageSize)

      const list = await find

      return {
        total,
        list,
        maxPage: Math.ceil(total / pageSize),
        page,
        pageSize,
      }
    }

    /**
     * 获取树形结构
     */
    Model.tree = async (options = {}) => {
      options.where && console.log('options.where 已废弃，请使用filter')

      let find = Model.find(options.where || options.filter)

      find = find.sort(options.sort)

      let docs = await find

      const list = {}
      const tree = []

      // 添加到一维对象和增加子元素数组
      if (docs) {
        // 提取元素下标到对象中保存
        docs = docs.map((doc) => {
          const item = doc.toJSON()
          item.children = []
          list[item[idKey]] = item
          return item
        })

        docs.map((doc) => {
          const { parentId } = doc
          const item = list[doc[idKey]]

          // 绑定子元素到父元素中
          if (parentId) {
            const parent = list[parentId]
            if (parent) parent.children.push(item)
          }
          // 添加父元素到树中
          else {
            tree.push(item)
          }
        })
      }

      return tree
    }

    // 获取一维的树
    Model.children = async (options = {}) => {
      options.where && console.log('options.where 已废弃，请使用filter')

      let find = Model.find(options.where || options.filter)

      find = find.sort(options.sort)

      const docs = await find

      const list = []

      docs.map((doc) => {
        if (!doc.parentId) getChildren(doc)
        return doc
      })

      /**
       * 获取子元素
       * @param {*} parent
       * @param {*} rank
       */
      function getChildren(parent, rank = 1) {
        list.push({
          ...parent.toJSON(),
          index: rank,
        })

        // 寻找子级
        docs.map((item, index) => {
          if (item.parentId && item.parentId.toString() == parent.id.toString()) {
            // docs.splice(index, 1);
            // 移除，不在遍历
            getChildren(item, rank + 1)
          }
        })
      }

      return list
    }

    /**
     * 队列查询
     * @param {Object} filter 查询条件
     * @param {Object} options
     * @param {Object} options.where 查询条件
     * @param {Number} [options.page] 开始页数
     * @param {Number} [options.pageSize] 条数
     * @param {Function} fn 队列任务处理函数
     */
    Model.findQueue = async (filter, options, fn) => {
      options = {
        page: 1,
        pageSize: 5000,
        sort: { [idKey]: -1 },
        beforeFind: (find) => find,
        ...options,
      }

      const { page, pageSize, sort, beforeFind } = options

      let find = Model.find(filter)
        .skip((page - 1) * pageSize)
        .limit(pageSize)
        .sort(sort)

      if (beforeFind) find = beforeFind(find)

      const docs = await find

      if (docs) {
        // await helper.queue(docs, fn)
        await fn(docs)

        if (docs.length == pageSize) {
          options.page++
          return Model.findQueue(options, fn)
        }
      }
    }

    // 聚合-分页查询
    Model.aggregatePage = async (options = {}, page = 1, pageSize = 20) => {
      page = Number(page) || 1
      pageSize = Number(pageSize) || 20

      if (page) options.push({ $skip: (page - 1) * pageSize })
      if (pageSize != -1) options.push({ $limit: pageSize })

      const data = await Model.aggregate([
        {
          $facet: {
            list: options,
            total: [...options, { $count: 'count' }],
          },
        },
      ])

      const total = data[0].total.length ? data[0].total[0].count : 0
      const returnData = {
        list: data[0].list,
        total,
        maxPage: Math.ceil(total / pageSize),
        page,
        pageSize,
      }

      return returnData
    }

    return Model
  }
}
